﻿using Microsoft.Xna.Framework;
using System;

namespace block_explorer
{
    class OldNoiseBuilder : IMapBuilder
    {
        private const float HEIGHT_SCALE = 80.0f;
        private const int HEIGHT_SEED = 43646;
        private const int CAVE_SEED1 = 12334;
        private const int CAVE_SEED2 = 43321;

        public OldNoiseBuilder()
        {
        }

        public Block GetBlock(Vector3 worldPosition)
        {
            bool solid = false;

            int surface = GetHeightAt(new Vector2(worldPosition.X, worldPosition.Z));
            bool underground = surface >= worldPosition.Y;
            if (underground)
            {
                const int seed1 = CAVE_SEED1;
                const int seed2 = CAVE_SEED2;
                const int octaves = 1;
                const double amplitude = 1.0;
                const double gain = 1.0;
                const double frequency = 1.0;
                const double lacunarity = 1.0;
                const float offset = 1.0f;
                const double scale = 0.1;

                // the greater the depth the more we allow caves...
                const float minCaveFactor = 1.0f;
                const float maxCaveFactor = 0.6f;
                const float maxDepth = 200.0f; // at 0 depth (surface) use minCaveFactor, at maxDepth use maxCaveFactor
                float depth = (float)surface - worldPosition.Y;
                float caveFactor = MathHelper.Lerp(minCaveFactor, maxCaveFactor, Math.Min(1.0f, depth / maxDepth));

                float f = PerlinNoise.ridgedMF3D(worldPosition.X * scale, worldPosition.Y * scale, worldPosition.Z * scale, seed1, octaves, amplitude, gain, frequency, lacunarity, offset) > caveFactor ? 1.0f : 0.0f;
                float d = PerlinNoise.ridgedMF3D(worldPosition.X * scale, worldPosition.Y * scale, worldPosition.Z * scale, seed2, octaves, amplitude, gain, frequency, lacunarity, offset) > caveFactor ? 1.0f : 0.0f;
                float e = 1.0f;
                float r = f * d * e;
                solid = r < 0.5f;
            }

            return new Block(solid ? Block.ContentType.DIRT : Block.ContentType.AIR);
        }

        public int GetHeightAt(Vector2 worldPosition)
        {
            const int seed = HEIGHT_SEED;

            double pn = 1.0;
            {
                const int octaves = 4;
                const float persistance = 0.6f;
                const float scale = 0.008f;
                pn = (PerlinNoise.noise2d_perlin(worldPosition.X * scale, worldPosition.Y * scale, seed, octaves, persistance) + 1.0f) / 2.0f;
            }

            double rmf = 1.0;
            {
                const int octaves = 2;
                const double amplitude = 0.1;
                const double gain = 0.7;
                const double frequency = 4.0;
                const double lacunarity = 2.0;
                const float offset = 1.0f;
                const double scale = 0.02;
                rmf = (PerlinNoise.ridgedMF2D(worldPosition.X * scale, worldPosition.Y * scale, seed, octaves, amplitude, gain, frequency, lacunarity, offset) + 1.0f) / 2.0f;
            }

            return (int)((pn + rmf) / 2.0 * HEIGHT_SCALE) + 0;
        }
    }
}
